#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include"interface.h"

int maj_no = 250;
int mi_no = 0;
char devname[] = "led_dev";
const int count = 5;
int number;
const int baseminor = 0;
struct cdev *pdev;
struct class *pclass;
unsigned int *pcon0=NULL;
unsigned int *pcon1=NULL;
unsigned int *pcon2=NULL;
unsigned int *pcon3=NULL;

unsigned int *pdata0 = NULL;
unsigned int *pdata1 = NULL;
unsigned int *pdata2 = NULL;
unsigned int *pdata3 = NULL;
MODULE_LICENSE("GPL");
MODULE_AUTHOR("XIAO WANG");
MODULE_DESCRIPTION("A simple character device driver");

#define LENGTH 9
char str[LENGTH] = "ABCDEFGH";


ssize_t led_dev_Read(struct file *f, char __user *buf, size_t count, loff_t *seek)
{
	unsigned len;
	//printk("led_dev_Read starting\n");
	if(count > LENGTH-1)
		count = LENGTH -1;
	len = copy_to_user(buf,str, count);
	//printk("led_dev_Read finished!\n");
	return 0;
}

ssize_t led_dev_Write(struct file *f, const char __user *buf, size_t count, loff_t *seek)
{
	unsigned long len;
	/printk("led_dev_Write starting\n");
	if(count > LENGTH-1)
		count = LENGTH-1;
	len = copy_from_user(str,buf,count);
	//printk("str = %s\n", str);
	//printk("led_dev_Write finished!\n");
	return 0;
}
int led_dev_Open( struct inode *p,  struct file *f)
{
	//printk("led_dev device open\n");
	return 0;
}

int led_dev_Close( struct inode *p,  struct file *f)
{
	//printk("led_dev device close\n");
	return 0;
}
long led_dev_Ioctl(struct file *f, unsigned int cmd,  unsigned long arg)
{
	unsigned int tmp0,tmp1,tmp2,tmp3;
	switch(cmd)
	{
		case LED_L:
			// led   on 
			tmp0 = ioread32(pdata0);
			tmp0 |= 1 << 7;
			iowrite32(tmp0, pdata0);

			
			tmp1 = ioread32(pdata1);
			tmp1 |= 1 ;
			iowrite32(tmp1, pdata1);

			
			tmp2 = ioread32(pdata2);
			tmp2 |= 1 << 4;
			iowrite32(tmp2, pdata2);
	
			
			tmp3 = ioread32(pdata3);
			tmp3 |= 1 << 5;
			iowrite32(tmp3, pdata3);
	
			break;
		case LED_D:

			// led   off 
			tmp0 = ioread32(pdata0);
			tmp0 &= ~(1 << 7);
			iowrite32(tmp0, pdata0);
	
			
			tmp1 = ioread32(pdata1);
			tmp1 &= ~(1);
			iowrite32(tmp1, pdata1);
	
			
			tmp2 = ioread32(pdata2);
			tmp2 &= ~(1 << 4);
			iowrite32(tmp2, pdata2);

			
			tmp3 = ioread32(pdata3);
			tmp3 &= ~(1 << 5);
			iowrite32(tmp3, pdata3);

			break;
	}
	return 0;
}
struct file_operations led_dev_OPS =
{
	.owner = THIS_MODULE,

	.read = led_dev_Read,
	.write = led_dev_Write,
	.open = led_dev_Open,
	.release = led_dev_Close,
	.unlocked_ioctl = led_dev_Ioctl,
};



int HelloDriverProbe(struct platform_device *pDevice)
{
	struct resource *res = pDevice->resource;
	int i;
	struct device *pdevice;
	unsigned int tmp0;
	unsigned int tmp1;
	unsigned int tmp2;
	unsigned int tmp3;
	
	pcon0 = ioremap(res[0].start,4);
	pdata0 = ioremap(res[1].start,4);
	tmp0 = ioread32(pcon0);
	tmp0 = (tmp0 & ~(0xf << 28)) | ( 1 << 28);
	iowrite32(tmp0, pcon0);
	
	pcon1 = ioremap(res[2].start,4);
	pdata1 = ioremap(res[3].start,4);
	tmp1 = ioread32(pcon1);
	tmp1 = (tmp1 & ~(0xf )) | ( 1 );
	iowrite32(tmp1, pcon1);
	
	pcon2 = ioremap(res[4].start,4);
	pdata2 = ioremap(res[5].start,4);
	tmp2 = ioread32(pcon2);
	tmp2 = (tmp2 & ~(0xf << 16)) | ( 1 << 16);
	iowrite32(tmp2, pcon2);
	
	pcon3 = ioremap(res[4].start,4);
	pdata3 = ioremap(res[5].start,4);
	tmp3 = ioread32(pcon3);
	tmp3 = (tmp3 & ~(0xf << 20)) | ( 1 << 20);
	iowrite32(tmp3, pcon3);
	

	
	
	if(alloc_chrdev_region(&number,baseminor,count, devname))
	{
		printk("number register faild\n");
		return -1;
	}	 
	maj_no = MAJOR(number);
	pdev = cdev_alloc();
	if( NULL == pdev)
		goto CDEV_ALLOC_ERR;
	cdev_init(pdev, &led_dev_OPS);
	if(cdev_add(pdev, number,count))
		goto CDEV_ADD_ERR;
	pclass = class_create(THIS_MODULE,"hello");
	if(IS_ERR(pclass))
		goto CLASS_CREATE_ERR;
	for(i = 0; i < count; i++)
	{
		pdevice = device_create(pclass, 0,MKDEV(maj_no,i),0, "led_dev%d",i);
		if(IS_ERR(pdevice))
			goto DEVICE_CREATE_ERR;	

	}
     //   printk("HelloDriverProbe\r\n");
	
	return 0;
DEVICE_CREATE_ERR:
	class_destroy(pclass);
CLASS_CREATE_ERR:
CDEV_ADD_ERR:
	cdev_del(pdev);
CDEV_ALLOC_ERR:
	unregister_chrdev_region(number, count);
	return -1;
	
}

int HelloDriverRemove(struct platform_device *pDevice)
{
	int i;
	//iounmap(pcon);
	//iounmap(pdata);
	for(i = 0; i < count; i++)
		device_destroy(pclass, MKDEV(maj_no, i));
	class_destroy(pclass);
	cdev_del(pdev);
	unregister_chrdev_region(number,count);
//	printk("exit module, cleanup_module\n");

        printk("HelloDriverRemove\r\n");
        return 0;
}
static const struct of_device_id  hello_of_match[] = {
	{.compatible = "fs4412,led"},
	{}
};

static struct platform_driver g_stPlantFormDriver =
{
        .driver = {
                .name = "hehe_platform",
                .owner = THIS_MODULE,
		.of_match_table = hello_of_match,
        },
        .probe  = HelloDriverProbe,
        .remove =  HelloDriverRemove,
};


int HelloInit(void)
{
        int iRet = 0;
      //  printk("driver,  module init enter\r\n");

        iRet = platform_driver_register(&g_stPlantFormDriver);
        if (0 != iRet)
        {
                printk("platform_driver_register error");
		return -1;
        }
      //  printk("driver,  module init exit\r\n");
        return iRet;

}

void HelloRelease(void)
{
      //  printk("driver,  clean module enter\r\n");
        platform_driver_unregister(&g_stPlantFormDriver);
       // printk("driver,   clean module exit\r\n");
}

module_exit(HelloRelease);
module_init(HelloInit);

MODULE_LICENSE("GPL");


